package com.springboot.frame.starter.tools;

import org.springframework.util.StringUtils;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Objects;

/**
 * @author liheng
 * @ClassName DateTimeZoneUtil
 * @Description
 * @date 2021-09-01 17:06
 */
public class DateTimeZoneUtil {
    /**
     * 系统默认时区
     */
    private static final ZoneId ZONE = ZoneId.systemDefault();
    /**
     * yyyy-MM-dd'T'HH:mm:ssxxx 比如：2020-05-23T17:06:30+08:00 0时区时末尾 为+00:00
     */
    public static final DateTimeFormatter YYYY_MM_DD_T_HH_MM_SS_XXX_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx");
    /**
     * yyyy-MM-dd HH:mm:ss 比如：2020-05-23 17:06:30
     */
    public static final DateTimeFormatter YYYY_MM_DD_HH_MM_SS_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZONE);

    /**
     * 时间转 TimeZone
     *
     * @param time
     * @return
     * @throws Exception
     */
    public static String dateToTimeZone(long time) throws Exception {
        return dateToTimeZone(new Date(time));
    }

    /**
     * 时间转 TimeZone
     *
     * @param date
     * @return
     * @throws Exception
     */
    public static String dateToTimeZone(Date date) throws Exception {
        String time;
        if (date == null) {
            throw new Exception("date is not null");
        }
        ZonedDateTime zonedDateTime = toZonedDateTime(date);
        time = format(zonedDateTime, YYYY_MM_DD_T_HH_MM_SS_XXX_FMT);
        return time;
    }

    /**
     * TimeZone 时间转标准时间
     *
     * @param str
     * @return
     * @throws Exception
     */
    public static String timeZoneDateToStr(String str) throws Exception {
        String time;
        if (!StringUtils.hasLength(str)) {
            throw new Exception("str is not null");
        }
        ZonedDateTime zonedDateTime = parseToZonedDateTime(str, YYYY_MM_DD_T_HH_MM_SS_XXX_FMT);
        if (zonedDateTime == null) {
            throw new Exception("str to zonedDateTime fail");
        }
        time = zonedDateTime.format(YYYY_MM_DD_HH_MM_SS_FMT);
        return time;
    }

    /**
     * 根据 formatter解析为 ZonedDateTime
     *
     * @param text      待解析字符串
     * @param formatter DateTimeFormatter
     * @return ZonedDateTime
     */
    public static ZonedDateTime parseToZonedDateTime(String text, DateTimeFormatter formatter) {
        return ZonedDateTime.parse(text, formatter);
    }

    /**
     * 根据 formatter格式化 date
     *
     * @param date      Date
     * @param formatter DateTimeFormatter
     * @return String
     */
    public static String format(Date date, DateTimeFormatter formatter) {
        Objects.requireNonNull(formatter, "formatter");
        return toZonedDateTime(date).format(formatter);
    }

    /**
     * 根据 dateFormatPattern格式化 date
     *
     * @param date              Date
     * @param dateFormatPattern 自定义格式化模板
     * @return String
     */
    public static String format(Date date, String dateFormatPattern) {
        Objects.requireNonNull(dateFormatPattern, "dateFormatPattern");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormatPattern).withZone(ZONE);
        return format(date, formatter);
    }


    /**
     * 根据 formatter格式化 localDateTime
     *
     * @param localDateTime LocalDateTime
     * @param formatter     DateTimeFormatter
     * @return String
     */
    public static String format(LocalDateTime localDateTime, DateTimeFormatter formatter) {
        Objects.requireNonNull(localDateTime, "localDateTime");
        Objects.requireNonNull(formatter, "formatter");
        return toZonedDateTime(localDateTime).format(formatter);
    }

    /**
     * 根据 dateFormatPattern格式化 localDateTime
     *
     * @param localDateTime     LocalDateTime
     * @param dateFormatPattern 自定义格式模板
     * @return String
     */
    public static String format(LocalDateTime localDateTime, String dateFormatPattern) {
        Objects.requireNonNull(localDateTime, "localDateTime");
        Objects.requireNonNull(dateFormatPattern, "dateFormatPattern");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormatPattern).withZone(ZONE);
        return format(localDateTime, formatter);
    }

    /**
     * 根据 formatter格式化 localTime
     *
     * @param localTime LocalTime
     * @param formatter DateTimeFormatter
     * @return String
     */
    public static String format(LocalTime localTime, DateTimeFormatter formatter) {
        Objects.requireNonNull(localTime, "localTime");
        Objects.requireNonNull(formatter, "formatter");
        return toZonedDateTime(localTime).format(formatter);
    }

    /**
     * 根据 formatter格式化 instant
     *
     * @param instant   Instant
     * @param formatter DateTimeFormatter
     * @return String
     */
    public static String format(Instant instant, DateTimeFormatter formatter) {
        Objects.requireNonNull(instant, "instant");
        Objects.requireNonNull(formatter, "formatter");
        return formatter.format(instant);
    }

    /**
     * 根据 formatter格式化 zonedDateTime
     *
     * @param zonedDateTime ZonedDateTime
     * @param formatter     DateTimeFormatter
     * @return String
     */
    public static String format(ZonedDateTime zonedDateTime, DateTimeFormatter formatter) {
        Objects.requireNonNull(zonedDateTime, "zonedDateTime");
        Objects.requireNonNull(formatter, "formatter");
        return zonedDateTime.format(formatter);
    }

    /**
     * Date转ZonedDateTime，时区为系统默认时区
     *
     * @param date Date
     * @return ZonedDateTime
     */
    public static ZonedDateTime toZonedDateTime(Date date) {
        Objects.requireNonNull(date, "date");
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault());
    }

    /**
     * Date转ZonedDateTime，可以直接转换为对应的时区
     *
     * @param date   Date 没有时区区分
     * @param zoneId 目标时区
     * @return ZonedDateTime
     */
    public static ZonedDateTime toZonedDateTime(Date date, String zoneId) {
        Objects.requireNonNull(zoneId, "zoneId");
        return toZonedDateTime(date, ZoneId.of(zoneId));
    }

    /**
     * Date转ZonedDateTime
     *
     * @param date Date
     * @param zone 目标时区
     * @return ZonedDateTime
     */
    public static ZonedDateTime toZonedDateTime(Date date, ZoneId zone) {
        Objects.requireNonNull(date, "date");
        Objects.requireNonNull(zone, "zone");
        return Instant.ofEpochMilli(date.getTime()).atZone(zone);
    }

    /**
     * LocalDateTime转ZonedDateTime，时区为系统默认时区
     *
     * @param localDateTime LocalDateTime
     * @return ZonedDateTime
     */
    public static ZonedDateTime toZonedDateTime(LocalDateTime localDateTime) {
        Objects.requireNonNull(localDateTime, "localDateTime");
        return localDateTime.atZone(ZoneId.systemDefault());
    }

    /**
     * LocalDateTime转ZonedDateTime，时区为zoneId对应时区<br>
     * 注意，需要保证localDateTime和zoneId是对应的，不然会出现错误<br>
     * 比如，localDateTime是巴黎时区下的对象，zoneId也应该是巴黎时区id<br>
     *
     * @param localDateTime LocalDateTime
     * @param zoneId        时区id
     * @return ZonedDateTime
     */
    public static ZonedDateTime toZonedDateTime(LocalDateTime localDateTime, String zoneId) {
        Objects.requireNonNull(localDateTime, "localDateTime");
        Objects.requireNonNull(zoneId, "zoneId");
        return localDateTime.atZone(ZoneId.of(zoneId));
    }

    /**
     * LocalTime转ZonedDateTime
     * 以当天的日期+LocalTime组成新的ZonedDateTime，时区为系统默认时区
     *
     * @param localTime LocalTime
     * @return ZonedDateTime
     */
    public static ZonedDateTime toZonedDateTime(LocalTime localTime) {
        Objects.requireNonNull(localTime, "localTime");
        return LocalDate.now().atTime(localTime).atZone(ZoneId.systemDefault());
    }

    /**
     * Instant转ZonedDateTime，时区为系统默认时区
     *
     * @param instant Instant
     * @return ZonedDateTime
     */
    public static ZonedDateTime toZonedDateTime(Instant instant) {
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).atZone(ZoneId.systemDefault());
    }

    /**
     * 时间戳epochMilli毫秒转ZonedDateTime，时区为系统默认时区
     *
     * @param epochMilli 时间戳
     * @return ZonedDateTime
     */
    public static ZonedDateTime toZonedDateTime(long epochMilli) {
        Objects.requireNonNull(epochMilli, "epochMilli");
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneId.systemDefault())
                .atZone(ZoneId.systemDefault());
    }

}
